import { InfoCircleOutlined } from '@ant-design/icons'
import { DndContext, DragOverlay, MouseSensor, TouchSensor, useSensor, useSensors } from '@dnd-kit/core'
import { rectSortingStrategy, SortableContext } from '@dnd-kit/sortable'
import { Button, Card, Col, Form, Input, Row } from 'antd'
import { nanoid } from 'nanoid'
import { useState } from 'react'

import { divide, multiply } from '@/utils/calculate'

import AekDrawer from '../AekDrawer'
import AekForm from '../AekForm'
import APanel from '../APanel'

import Drag from './components/Drag'
import DraggableItem from './components/DraggableItem'
import Droppable from './components/Droppable'
import FormItemPropertiesEditor from './components/FormItemPropertiesEditor'
import SortableItem from './components/SortableItem'
import { formItemList } from './constants'

/**
 * 表单构建器组件，用于创建和编辑表单
 */
const FormBuilder = () => {
  // 可用表单项状态
  const [availableItems, setAvailableItems] = useState(formItemList)
  // 已添加的表单项
  const [formItems, setFormItems] = useState([])
  // 当前选中的表单项
  const [selectedItem, setSelectedItem] = useState()
  // 表单标题
  const [formTitle, setFormTitle] = useState('我的表单')
  // 当前正在拖拽的表单项
  const [curentDrag, setCurentDrag] = useState()

  /** 配置拖拽传感器 */
  const sensors = useSensors(
    useSensor(MouseSensor, {
      activationConstraint: {
        // 添加该值可以让点击事件生效
        distance: 5,
      },
    }),
    useSensor(TouchSensor),
  )

  /**
   * 处理拖拽源表单项的选择事件，将选中的表单项添加到已添加的表单项列表中
   * @param {Object} item - 选中的表单项对象
   */
  const handleDraggableItemSelect = item => {
    setFormItems([...formItems, { ...item, id: `${item.type}-${nanoid()}` }])
  }

  /**
   * 处理拖拽开始事件，记录当前正在拖拽的表单项
   * @param {Object} event - 拖拽事件对象
   */
  const handleDragStart = event => {
    const { active } = event
    // 当从左侧拖入到右侧表单区域时，保存当前拖拽项
    const availableItem = availableItems.find(item => item.id === active.id)
    if (!availableItem) return
    setCurentDrag({ ...availableItem })
  }

  /**
   * 处理拖拽到某个元素上面的事件，根据拖拽位置更新表单项列表
   * @param {Object} event - 拖拽事件对象
   */
  const handleDragOver = event => {
    const { active, over } = event
    // 只有当从左侧拖入到右侧表单区域时，才会走后续逻辑
    if (!curentDrag) return
    /** 右侧表单区域, 包括右侧表单区域内的表单项 */
    const inRightArea = over?.id === 'form-container' || formItems.find(item => item.id === over?.id)
    // 已经添加到表单项列表中，不再添加
    if (curentDrag.added) {
      const _formItems = [...formItems]
      const finded = _formItems.find(item => item.id === active.id)
      // 当前处于隐藏状态，移入到右侧表单区域，将当前拖拽项标记显示
      if (finded.hide && inRightArea) {
        finded.hide = false
        setFormItems(_formItems)
      }
      // 当前处于显示状态，移出右侧表单区域，将当前拖拽项标记隐藏（因为删除的话会影响拖拽组件的逻辑判断，所以用隐藏）
      if (!finded.hide && !inRightArea) {
        finded.hide = true
        setFormItems(_formItems)
      }
    } else {
      // 移入右侧表单区域，将当前拖拽项添加到表单项列表中
      curentDrag.added = true
      setFormItems([...formItems, { ...curentDrag }])
    }
  }

  /**
   * 处理拖拽结束事件，更新表单项列表和可用表单项列表
   * @param {Object} event - 拖拽事件对象
   */
  const handleDragEnd = event => {
    setCurentDrag()
    const { active, over } = event
    const newItems = [...formItems]
    if (over && active.id !== over.id) {
      const oldIndex = formItems.findIndex(item => item.id === active.id)
      const newIndex = formItems.findIndex(item => item.id === over.id)

      // 交换位置
      if (oldIndex !== -1 && newIndex !== -1) {
        newItems.splice(oldIndex, 1)
        newItems.splice(newIndex, 0, formItems[oldIndex])
      }
    }
    if (curentDrag) {
      const finded = newItems?.find(item => item.id === curentDrag.id)
      if (finded) {
        // 如果已经标记为隐藏，则需要删除元素
        if (finded.hide) {
          newItems.splice(
            newItems.findIndex(item => item.id === curentDrag.id),
            1,
          )
        } else {
          // 更新id，避免与左侧表单项的id重复
          finded.id = `${finded.type}-${nanoid()}`
        }
      }
      // 左侧置空再重新赋值，让id重新生效
      const _availableItems = availableItems
      setAvailableItems([])
      setTimeout(() => {
        setAvailableItems(_availableItems)
      }, 0)
    }
    setFormItems(newItems)
  }

  /**
   * 处理表单项属性变更事件，更新已添加的表单项列表和当前选中的表单项
   * @param {Object} updatedItem - 更新后的表单项对象
   */
  const handlePropertyChange = updatedItem => {
    setFormItems(prevItems => prevItems.map(item => (item.id === updatedItem.id ? updatedItem : item)))

    if (selectedItem && selectedItem.id === updatedItem.id) {
      setSelectedItem(updatedItem)
    }
  }

  /**
   * 处理表单项选择事件，更新当前选中的表单项
   * @param {Object} item - 选中的表单项对象
   */
  const handleItemSelect = item => {
    setSelectedItem(item)
  }

  /**
   * 处理表单项删除事件，从已添加的表单项列表中移除指定的表单项
   * @param {string} id - 要删除的表单项的 ID
   */
  const handleItemDelete = id => {
    setFormItems(prevItems => prevItems.filter(item => item.id !== id))
    if (selectedItem && selectedItem.id === id) {
      setSelectedItem(null)
    }
  }

  /**
   * 处理表单提交事件，打印表单结构的 JSON 数据
   */
  const handleFormSubmit = () => {
    const formData = {
      title: formTitle,
      fields: formItems.map(item => ({
        id: item.id,
        type: item.type,
        properties: item.properties,
      })),
    }

    console.log('表单结构JSON:', JSON.stringify(formData, null, 2))
  }

  /**
   * 渲染表单预览界面
   */
  const previewForm = () => {
    AekDrawer.PromiseThen({
      title: '表单预览',
      width: 1000,
      showFooter: false,
      children: (
        <>
          <APanel title={formTitle}></APanel>
          <AekForm
            formData={formItems.map(
              ({ aekComName, properties: { width, label, required, defaultValue, ...otherProps } }) => ({
                field: `type-${nanoid()}`,
                col: Math.ceil(divide(multiply(width.replace('%', ''), 24), 100)),
                label,
                formItemProps: {
                  initialValue: defaultValue,
                  rules: [{ required }],
                },
                component: { name: aekComName, props: { ...otherProps } },
              }),
            )}
          />
        </>
      ),
    })
  }

  return (
    <DndContext
      sensors={sensors}
      // collisionDetection={closestCenter}
      // modifiers={[restrictToVerticalAxis]}
      onDragStart={handleDragStart}
      onDragEnd={handleDragEnd}
      onDragOver={handleDragOver}
    >
      <Row gutter={16} className='h-full'>
        <Col className='min-w-220px h-full'>
          <Card title='可用表单项' className='h-full'>
            {availableItems.map(item => (
              <DraggableItem key={item.id} item={item} onItemSelect={handleDraggableItemSelect} />
            ))}
          </Card>
        </Col>
        <Col className='flex-grow-1 w-0'>
          <Card
            title='表单设计器'
            className='flex h-full flex-col'
            classNames={{
              body: 'flex-grow-1 flex flex-col',
            }}
          >
            <Form.Item label='表单标题'>
              <Input value={formTitle} onChange={e => setFormTitle(e.target.value)} />
            </Form.Item>
            <Droppable id='form-container' className='flex-grow-1 h-0'>
              <SortableContext
                items={formItems.map(item => item.id)}
                // 适用场景：矩形网格布局，比如 flex 容器内部配置 flex-wrap: wrap 换行之后，可以采用这种策略
                strategy={rectSortingStrategy}
              >
                <div className='b-rd-4px b-1px b-dashed b-#ccc p16px h-full overflow-y-auto overflow-x-hidden'>
                  {formItems.length === 0 ? (
                    <div style={{ textAlign: 'center', padding: '40px 0', color: '#999' }}>
                      <InfoCircleOutlined style={{ marginRight: 8 }} />
                      从左侧拖入表单项到此处
                    </div>
                  ) : (
                    <Row gutter={[10, 10]} className='overflow-hidden'>
                      {formItems.map(item => (
                        <Col key={item.id} style={{ width: item.properties.width }}>
                          <SortableItem
                            item={item}
                            selectedItem={selectedItem}
                            onItemSelect={handleItemSelect}
                            onItemDelete={handleItemDelete}
                          />
                        </Col>
                      ))}
                    </Row>
                  )}
                </div>
              </SortableContext>
            </Droppable>
            <div className='mt10px flex justify-end'>
              <Button type='primary' onClick={previewForm}>
                预览
              </Button>
              <Button type='primary' onClick={handleFormSubmit}>
                提交表单
              </Button>
            </div>
          </Card>
        </Col>
        <Col className='w-380px h-full'>
          <Card
            title={`【${selectedItem ? selectedItem.title : ''}】属性设置`}
            className='flex h-full flex-col'
            classNames={{
              body: 'flex-grow-1 flex flex-col h-0 overflow-y-auto',
            }}
          >
            {selectedItem ? (
              <FormItemPropertiesEditor item={selectedItem} onPropertyChange={handlePropertyChange} />
            ) : (
              <div className='py40px c-#999 text-center'>
                <InfoCircleOutlined className='mr8px' />
                请选择一个表单项进行属性设置
              </div>
            )}
          </Card>
        </Col>
      </Row>
      {curentDrag && (
        <DragOverlay>
          <Drag item={curentDrag} />
        </DragOverlay>
      )}
    </DndContext>
  )
}

export default FormBuilder
